//
//  CantripStateMachine.m
//  CantripSDK
//
//  Created by James Callender on Thu Apr 25 2002.
//  Copyright (c) 2001 James Callender. All rights reserved.
//

#import "CantripStateMachine.h"


@implementation CantripStateMachine

- (id)init
{
    if (self = [super init])
    {
	int i;
	mState = ANSI_STATE_GROUND;
	numParams = 0;
	for (i = 0; i < 16; i++)
		paramValue[i] = 0;
	param[0] = 0;
	collect[0] = 0;
    }
    return self;
}

- (unsigned int)state
{
    return mState;
}

- (int)step:(unsigned char)c
{
    unsigned int v;

    v = [self process:c];
    switch(v)
    {
	case ANSI_ACTION_PARAM:
	    if (c == ';')
	    {
		// Behavior for empty parameters change for different emulation.
		// I've chosen to just interpret it as a '0' which is more
		// consistant with MUDs
		if (param[0] == 0) paramValue[numParams] = 0;
		else paramValue[numParams] = atoi(param);
		numParams++;
		param[0] = 0;
	    }
	    else
	    {
		param[strlen(param)+1] = 0;
		param[strlen(param)] = c;
	    }
	    break;
	case ANSI_ACTION_COLLECT:
	    collect[strlen(collect)] = c;
	    collect[strlen(collect)+1] = 0;
	    break;
	case ANSI_ACTION_DISPATCH:
	    if (param[0] != 0)
	    {
		paramValue[numParams] = atoi(param);
		numParams++;
	    }
	    //NSLog(@"%d: %d", numParams, paramValue[0]);
	    break;
    }

    return v;
}

- (int)process:(unsigned char)c
{   
    // Handle 'Anywhere' states here
    if (c == ESC_CHAR) { // Escape character
	numParams = 0;
	param[0] = 0;
	collect[0] = 0;

	mState = ANSI_STATE_ESC;
	return ANSI_ACTION_IGNORE;
    } else if (c == 24 || c == 26) {
	mState = ANSI_STATE_GROUND;
	return ANSI_ACTION_EXECUTE;
    } else if ((c >= 128 && c <= 143) || (c >= 145 && c <= 151) || c == 153 || c == 154 || c == 156) {
	mState = ANSI_STATE_GROUND;
	return ANSI_ACTION_EXECUTE;
    } else if (c == 152 || c == 158 || c == 159) {
	mState = ANSI_STATE_SOS_PM_APC_STR;
	return ANSI_ACTION_IGNORE;
    } else if (c == 144) {
	mState = ANSI_STATE_DCSENTRY;
	return ANSI_ACTION_IGNORE;
    } else if (c == 155) {
	mState = ANSI_STATE_CSIENTRY;
	return ANSI_ACTION_IGNORE;
    } else if (c == 157) {
	mState = ANSI_STATE_OSCSTR;
	return ANSI_ACTION_OSC_START;
    }

    // Handle specific states
    switch (mState)
    {
	case ANSI_STATE_GROUND:
	    if ((c >= 0 && c <= 17) || c == 19 || (c >= 28 && c <= 31))
		    return ANSI_ACTION_EXECUTE;
	    else if (c >= 32 && c <= 127)
		    return ANSI_ACTION_PRINT;
	    else if ((c >= 128 && c <= 144) || (c >= 145 && c <= 154) || c == 156)
		    return ANSI_ACTION_EXECUTE;
	    break;
	case ANSI_STATE_ESC:
	    if (c == 128) return ANSI_ACTION_IGNORE;
	    if ((c >= 0 && c <= 17) || c == 19 || (c >= 28 && c <= 31)) return ANSI_ACTION_EXECUTE;
	    switch (c)
	    {
		case 80:
		    mState = ANSI_STATE_DCSENTRY;
		    return ANSI_ACTION_IGNORE;
		    break;
		case 91:
		    mState = ANSI_STATE_CSIENTRY;
		    return ANSI_ACTION_IGNORE;
		    break;
		case 93:
		    mState = ANSI_STATE_OSCSTR;
		    return ANSI_ACTION_OSC_START;
		    break;
		case 88:
		case 94:
		case 95:
		    mState = ANSI_STATE_SOS_PM_APC_STR;
		    return ANSI_ACTION_IGNORE;
		    break;
	    }
	    if (c >= 32 && c <= 47)
	    {
		mState = ANSI_STATE_ESCINTERM;
		return ANSI_ACTION_COLLECT;
	    }
	    if ((c >= 48 && c <= 79) || (c >= 81 && c <= 87) || 
	     c == 89 || c == 90 || c == 92 || (c >= 96 || c <= 126))
	    {
		mState = ANSI_STATE_GROUND;
		return ANSI_ACTION_DISPATCH;
	    }
	    break;
	case ANSI_STATE_ESCINTERM:
	    if (c >= 48 && c <= 126)
	    {
		mState = ANSI_STATE_GROUND;
		return ANSI_ACTION_DISPATCH;
	    }
	    if ((c >= 0 && c <= 23) || c == 25 || (c >= 28 && c <= 31))
		return ANSI_ACTION_EXECUTE;
	    if (c >= 32 && c <= 47) return ANSI_ACTION_COLLECT;
	    if (c == 127) return ANSI_ACTION_IGNORE;
	    break;
	case ANSI_STATE_CSIENTRY:
	    if ((c >= 0 && c <= 23) || c == 19 || (c >= 28 && c <= 31)) return ANSI_ACTION_EXECUTE;
	    if (c == 127) return ANSI_ACTION_IGNORE;
	    if ((c >= 48 && c <= 57) || c == 59)
	    {
		mState = ANSI_STATE_CSIPARAM;
		return ANSI_ACTION_PARAM;
	    }
	    if (c >= 60 && c <= 63)
	    {
		mState = ANSI_STATE_CSIPARAM;
		return ANSI_ACTION_COLLECT;
	    }
	    if (c >= 32 && c <= 47)
	    {
		mState = ANSI_STATE_CSIINTERM;
		return ANSI_ACTION_COLLECT;
	    }
	    if (c >= 64 && c <= 126)
	    {
		mState = ANSI_STATE_GROUND;
		return ANSI_ACTION_DISPATCH;
	    }
	    break;
	case ANSI_STATE_CSIIGNORE:
	    if ((c >= 1 && c <= 23) || c == 25 || (c >= 28 && c <= 31)) return ANSI_ACTION_EXECUTE;
	    if ((c >= 32 && c <= 63) || c == 127) return ANSI_ACTION_IGNORE;
	    if (c >= 64 && c <= 126)
	    {
		mState = ANSI_STATE_GROUND;
		return ANSI_ACTION_IGNORE;
	    }
	    break;
	case ANSI_STATE_CSIPARAM:
	    if ((c >= 0 && c <= 23) || c == 25 || (c >= 28 && c <= 31)) return ANSI_ACTION_EXECUTE;
	    if ((c >= 48 && c <= 57) || c == 59) return ANSI_ACTION_PARAM;
	    if (c == 127) return ANSI_ACTION_IGNORE;
	    if (c == 58 || (c >= 60 && c <= 63))
	    {
		mState = ANSI_STATE_CSIIGNORE;
		return ANSI_ACTION_IGNORE; // Process? return [self process:c];
	    }
	    if (c >= 64 && c <= 126)
	    {
		mState = ANSI_STATE_GROUND;
		return ANSI_ACTION_DISPATCH;
	    }
	    if (c >= 32 && c <= 47)
	    {
		mState = ANSI_STATE_CSIINTERM;
		return ANSI_ACTION_COLLECT;
	    }
	    break;
	case ANSI_STATE_CSIINTERM:
	    if ((c >= 0 && c <= 23) || c == 19 || (c >= 28 && c <= 31)) return ANSI_ACTION_EXECUTE;
	    if (c >= 32 && c <= 47) return ANSI_ACTION_COLLECT;
	    if (c == 127) return ANSI_ACTION_IGNORE;
	    if (c >= 48 && c <= 63)
	    {
		mState = ANSI_STATE_CSIIGNORE;
		return ANSI_ACTION_IGNORE; // Process? return [self process:c];
	    }
	    if (c >= 64 && c <= 126)
	    {
		mState = ANSI_STATE_GROUND;
		return ANSI_ACTION_DISPATCH;
	    }
	    break;
	// The following are currently not implemented/handled
	// (why bother for MUDs?)
	case ANSI_STATE_DCSENTRY:
		// 00-17, 19, 1C-1F
		// 7F
		if (c >= 64 && c <= 126) mState = ANSI_STATE_DCSPASSTHROUGH;
		if (c >= 48 && c <= 57) 
		{
			mState = ANSI_STATE_DCSPARAM;
			return ANSI_ACTION_PARAM;
		}
		if (c >= 60 && c <= 64)
		{
			mState = ANSI_STATE_DCSPARAM;
			return ANSI_ACTION_COLLECT;
		}
		if (c == 58) mState = ANSI_STATE_DCSIGNORE;
		if (c >= 32 && c <= 47)
		{
			mState =  ANSI_STATE_DCSINTERM;
			return ANSI_ACTION_COLLECT;
		}
		return ANSI_ACTION_IGNORE;
		break;
	case ANSI_STATE_DCSPARAM:
		if (c >= 0 && c <= 23 || c == 25 || c >= 28 && c <= 31) return ANSI_ACTION_IGNORE;
		if (c >= 48 && c <= 57 || c == 59) return ANSI_ACTION_PARAM;
		if (c == 127) return ANSI_ACTION_IGNORE;
		if (c >= 64 && c <= 126)
		{
			mState = ANSI_STATE_DCSPASSTHROUGH;
			return ANSI_ACTION_HOOK;
		}
		if (c == 58 || c >= 60 && c <= 63)
		{
			mState = ANSI_STATE_DCSIGNORE;
			return ANSI_ACTION_IGNORE;
		}
		if (c >= 32 && c <= 47)
		{
			mState = ANSI_STATE_DCSINTERM;
			return ANSI_ACTION_COLLECT;
		}
		break;
	case ANSI_STATE_DCSPASSTHROUGH:
		if (c >= 0 && c <= 23 || c == 25 || c >= 28 && c <= 31 || c >= 32 && c <= 126) return ANSI_ACTION_PUT;
		if (c == 127) return ANSI_ACTION_IGNORE;
		if (c == 156)
		{
			mState = ANSI_STATE_GROUND;
			return ANSI_ACTION_UNHOOK;
		}
		break;
	case ANSI_STATE_DCSIGNORE:
		if (c >= 0 && c <= 23 || c == 25 || c >= 28 && c <= 31 || c >= 32 && c <= 127) return ANSI_ACTION_IGNORE;
		if (c == 156) 
		{
			mState = ANSI_STATE_GROUND;
			return ANSI_ACTION_IGNORE;
		}
		break;
	case ANSI_STATE_DCSINTERM:
		if (c >= 48 && c <= 63)
		{
			mState = ANSI_STATE_DCSIGNORE;
			return ANSI_ACTION_IGNORE;
		}
		if (c >= 64 && c <= 126)
		{
			mState = ANSI_STATE_DCSPASSTHROUGH;
			return ANSI_ACTION_IGNORE;
		}
		break;
	case ANSI_STATE_OSCSTR:
		if (c >= 0 && c <= 23 || c == 25 || c >= 28 && c <= 31) return ANSI_ACTION_IGNORE;
		if (c >= 32 && c <= 127) return ANSI_ACTION_OSC_PUT;
		if (c == 156)
		{
			mState = ANSI_STATE_GROUND;
			return ANSI_ACTION_OSC_END;
		}
		break;
	case ANSI_STATE_SOS_PM_APC_STR:
	// 00-17,19,1C-1F,20-7F - ignore
		if (c == 156)
		{
			mState = ANSI_STATE_GROUND;
			return ANSI_ACTION_IGNORE;
		}
		break;
    }

    return ANSI_ACTION_IGNORE;
}

- (unsigned int)numberOfParams
{
	return numParams;
}

- (int)paramAtIndex:(unsigned int)i
{
	return paramValue[i];
}

@end
